import thread, time, Image, sys
from hough import hough
from histogram import histogram
from ctypes import *
from opencv.cv import * 
from opencv.highgui import *
from UI import chess_window

class Vision(object):
	def __init__(self, verbose=False):
		self.verbose = verbose
		#thread.start_new_thread(self.observeBoard, (1,))
		b = BoardID(self.getPicture(), self.verbose)
		
	def observeBoard(self, sleeptime):
		while 1:
			pic = self.getPicture()
			move = self.getMove(pic)
			if self.valid(pic) and move != None:
				## FIRE move event
				print "legal move"
			time.sleep(sleeptime)

	def getPicture(self):
		image = cvLoadImage("testpics/1-sm.jpg", 0)
		return image
		#capture picture

	def getColor(self, pic):
		#return color of piece in picture or None
		return 1

	def valid(self, picture):
		#if no hand in picture return 1
		return 1

	def getMove(self, picture):
		current = self.getState(picture)
		prev = self.session.LegalBoard
		return prev.getMoveTo(current)

	def getState(self, picture):
		cells = self.split(picture)
		state = []
		for x in range(8):
			state.append([])
			for y in range(8):
				state[x][y] = self.getColor(cells[x][y])
		return state

class BoardID(object):
	def __init__(self, image, verbose=False):
		self.verbose = verbose
		if self.verbose: self.testVision(image)

	def testVision(self, image):
		if self.verbose: print "testing vision"
		orig = cvCloneImage(image)
		vertical, horizontal = self.getHoughLines(image)
		horizontal.sort(self.compareY)
		
		# "drawing hough lines"
		self.hough.drawLines(horizontal)
		self.hough.showImage()

		enhancedHorizontals = self.fixHorizontal(horizontal)
		
		# "drawing enhanced lines"
		self.hough.drawLines(enhancedHorizontals)
		self.hough.showImage()

		self.hough.drawLines(vertical)
		self.hough.showImage()

		enhancedVerticals = self.genVerticals(enhancedHorizontals)

		self.hough.drawLines(enhancedVerticals)
		self.hough.showImage()

		self.hough.drawLines(enhancedHorizontals + enhancedVerticals)
		self.hough.showImage()

		self.h = enhancedHorizontals
		self.v = enhancedVerticals

		coords = [self.getLocationCoordinates("a1"),
			  self.getLocationCoordinates("h8"),
			  self.getLocationCoordinates("g7"),
			  self.getLocationCoordinates("f5"),
			  self.getLocationCoordinates("e3"),
			  self.getLocationCoordinates("d2"),
			  self.getLocationCoordinates("c2"),
			  self.getLocationCoordinates("b1")]

		self.hough.drawBoxes(coords)
		self.hough.showImage()

		for x in ["a7", "a2", "c1", "h1"]:
			subImg = self.getSquareImage(self.getLocationCoordinates(x), image)
			hData = self.getHistogram(subImg)
			if self.verbose: print self.isOccupied(hData)
		
		self.display = chess_window()
		for piece in self.getBoardState(image):
			self.display.addPiece(piece[0], piece[1])
		self.display.start()

	def isOccupied(self, histogram):
		minimum = 13
		return len(histogram) > minimum

	def getBoardState(self, image):
		pieces = []
		for x in "abcdefgh":
			for y in "87654321":
				color = self.getPieceAt(x + y, image)
				if color != None:
					pieces.append([x + y, color])
		return pieces

	def getPieceAt(self, place, image):
		subImg = self.getSquareImage(self.getLocationCoordinates(place), image)
		#h = hough(subImg)
		#circle = h.getCircle()
		circle = None
		hData = self.getHistogram(subImg)
		if self.isOccupied(hData):
			circle = 1
		return self.getColor(circle, subImg, hData, place)

	def getColor(self, piece, image, histogram, place):
		if piece == None:
			return None
		else:
			x, y = AtoI(place[0]), int(place[1])
			if x%2 != y%2:  color = "white"
			else:           color = "black"

			b, w = 0, 0
			for value in histogram:
				if value[0] < 25:  b += value[1]
				else:              w += value[1]
			if color == "black":
				if w > 350:  return "white"
				else:        return "black"
			else:
				if b > 400:  return "black"
				else:        return "white"

	def examineSquare(self, square, image):
		image = self.getSquareImage(self.getLocationCoordinates(square), image)
		self.showImage(image)

	def showImage(self, image):
		cvNamedWindow("image viewer", 1)
		cvShowImage("image viewer", image)
		cvWaitKey(0)

	def getSquareImage(self, coords, image):
		width, height = coords[1].x - coords[0].x, coords[1].y - coords[0].y 
		cropped = cvCreateImage( cvSize(width, height), 8, 1)
		src_region = cvGetSubRect(image, None, cvRect(coords[0].x, coords[0].y, width, height))
		cvCopy(src_region, cropped)

		return cropped

	def genVerticals(self, horizontals):
		verticals = []
		xmin, xmax = horizontals[0][0][0], horizontals[8][1][0]
		ymin, ymax = horizontals[0][0][1], horizontals[8][0][1]
		dist = (xmax - xmin) / 8
		
		for x in range(9):
			verticals.append(makeVerticalLine(xmin + (x * dist), ymin, ymax))
		
		return verticals

	def getLocationCoordinates(self, boardLocation):
		x, y = AtoI(boardLocation[0]), int(boardLocation[1])
		
		topleft = cvPoint(self.v[x-1][0][0], self.h[8-y][0][1])
		bottomRight = cvPoint(self.v[x][0][0], self.h[9-y][0][1])
		
		return cropRectangle(topleft, bottomRight, 5)

	def getLeftBorder(self, horizontal):
		leftGuesses = []
		for line in horizontal:
			leftGuesses.append(self.getSmallestX(line))

		leftGuess = self.getLikeliest(leftGuesses)
		if self.verbose: print "Left Guess: %s" % leftGuess
		return leftGuess

	def getRightBorder(self, horizontal):
		rightGuesses = []
		for line in horizontal:
			rightGuesses.append(self.getLargestX(line))

		rightGuess = self.getLikeliest(rightGuesses)
		if self.verbose: print "Right Guess: %s" % rightGuess
		return rightGuess

	def fixHorizontal(self, lines):
		left = self.getLeftBorder(lines)
		right = self.getRightBorder(lines)
		
		height = self.getCellHeight(lines)

		ret = []
		prevY = lines[0][0][1] - height
		for line in lines:
			#print "line %s" % line
			if len(ret) > 8:
				return ret
			if abs(line[0][0] - left) > 5:
				#print "fixing left at %s" % line[0][1]
				line[0][0] = left
			if abs(line[1][0] - right) > 5:
				#print "fixing right at %s" % line[0][1]
				line[1][0] = right
			currY = line[0][1]

			#add new lines if skip detected
			#print "Curry, height, prev, diff: %s" % [currY, height, prevY, currY-height-prevY] 
			if abs(currY - height - prevY) < 10:
				ret.append(line)
				prevY = currY
			else:
				diff = currY - height - prevY
				while diff > 10:
					#print "##################### DIFF: %s" % diff
					if self.verbose: print "adding new horizontal at %s" % (prevY + height)
					ret.append([[left, prevY + height], 
						    [right, prevY + height]])
					diff-=height
					prevY+=height
				ret.append(line)
				prevY = line[0][1]
		return ret

	def getCellHeight(self, horizontal):
		deltas = []
		for i in range(len(horizontal) - 1):
			deltas.append(abs(horizontal[i][0][1] - horizontal[i+1][0][1]))
		return self.getLikeliest(deltas)

	def compareX(self, l1, l2):
		return cmp(l1[0][0], l2[0][0])

	def compareY(self, l1, l2):
		return cmp(l1[0][1], l2[0][1])

	def getLikeliest(self, guesses):
		likelihood = 0.0
		likeliest = guesses[0]
		for guess in guesses:
			myLikelihood = 0.0
			for compare in guesses:
				diff = abs(guess - compare) + 0.1
				if diff < 5.0:
					myLikelihood+=1/diff
			if myLikelihood > likelihood:
				likelihood = myLikelihood
				likeliest = guess
		return likeliest

	def getSmallestX(self, line):
		return min(line[0][0], line[1][0])

	def getLargestX(self, line):
		return max(line[0][0], line[1][0])

	def getLargestY(self, line):
		return max(line[0][1], line[1][1])
	
	def getSmallestY(self, line):
		return min(line[0][1], line[1][1])

	def getHistogram(self, image):
		self.hist = histogram(image)
		return self.hist.getHist()

	def getHoughLines(self, image):
		self.hough = hough(image)
		self.hough.getProbabilistic()
		if self.verbose: self.hough.showImage()
		
		verti, horiz = [], []
		for line in self.hough.lines:
			if slope(line) > 3:
				if self.verbose: print "vertical: %s" % line
				verti.append(line)
			elif slope(line) < (0.2):
				if self.verbose: print "horizontal: %s" % line
				horiz.append(line)
		return verti, horiz

def slope(points):
	x1, y1 = points[0][0], points[0][1]
	x2, y2 = points[1][0], points[1][1]
	try:
		return (abs(y2 - y1) / abs(x2 - x1))
	except:
		return 100

def cropRectangle(p1, p2, num):
	return cvPoint(p1.x + num, p1.y + num), cvPoint(p2.x - num, p2.y - num)

def AtoI(char):
	return ord(char.upper()) - 64

def makeVerticalLine(x, y1, y2):
	return [[x, y1], [x, y2]]

